﻿/*
 * GLContainerBase.cpp
 *
 *  Created on: Jun 16, 2014
 *      Author: yuliang
 */

#include "GLContainerBase.h"
#include "GLGlobalDefine.h"

#define CHECK_CONTAINER_INDEX(box, index) { \
	if (box > m_BoxValidSize || box == 0 || box > MAX_CONTAINER_BOX_SIZE || index >= MAX_CONTAINER_BOX_INDEX_SIZE) { \
		LOG_ERROR("error|container|check|index|box|" << (uint32_t)box << \
				"|index|" << (uint32_t)index << "|valid|" << (uint32_t)m_BoxValidSize); \
		return GLContainerErrnoInvalidIndex; \
	} \
}

#define CHECK_CONTAINER_INDEX_PTR(box, index) { \
	if (box > m_BoxValidSize || box == 0 || box >= MAX_CONTAINER_BOX_SIZE || index >= MAX_CONTAINER_BOX_INDEX_SIZE) { \
		LOG_ERROR("error|container|check|index|box|" << (uint32_t)box << \
				"|index|" << (uint32_t)index << "|valid|" << (uint32_t)m_BoxValidSize); \
		return nullptr; \
	} \
}

CGLContainerBase::CGLContainerBase(UserId_t uid, Type_t type) :
	CGLEntityBase(GLEBTContainer), m_Uid(uid), m_ContainerType(type),
	m_BoxTotalSize(0), m_BoxValidSize(0) {
	// TODO Auto-generated constructor stub

}

CGLContainerBase::~CGLContainerBase() {
	// TODO Auto-generated destructor stub
	const MapEntityIndex_t* tAll = GetRealChilds();
	for (MapEntityIndex_t::const_iterator it = tAll->begin(); it != tAll->end(); ++it) {
		CGLEntityBase* tEntity = it->first;
		setGoods(dynamic_cast<CGLGoodsBase*>(tEntity));
	}
}

int32_t CGLContainerBase::InitBox(uint8_t size) {
	if (size > MAX_CONTAINER_BOX_SIZE)
		return GLContainerErrnoMaxBoxSize;
	uint32_t tTotalSize = size * MAX_CONTAINER_BOX_INDEX_SIZE;
	int32_t tRet = InitChildSize(tTotalSize);
	if (tRet != CommonErrnoSucceed) {
		LOG_ERROR("error|container|init|total|size|" << tTotalSize << "|box|size|" << (uint32_t)size << "|errno|" << tRet);
		return tRet;
	}

	m_BoxTotalSize = size;
	m_BoxValidSize = size;
	return CommonErrnoSucceed;
}

int32_t CGLContainerBase::ExpandBox(uint8_t size) {
	if (size == 0)
		return CommonErrnoSucceed;
	if (m_BoxTotalSize + size > MAX_CONTAINER_BOX_SIZE)
		return GLContainerErrnoMaxBoxSize;

	int32_t tRet = ExpandChildSize(size * MAX_CONTAINER_BOX_INDEX_SIZE);
	if (CommonErrnoSucceed != tRet) {
		LOG_ERROR("error|container|expand|size|" << (int32_t)(size * MAX_CONTAINER_BOX_INDEX_SIZE) <<
				"|curSize|" << GetChildSize());
		return tRet;
	}

	m_BoxTotalSize += size;
	return CommonErrnoSucceed;
}

bool CGLContainerBase::IsEnough(const GoodsInfo& info) const {
	Number_t tNeedNum = getNeedGoodsNumber(info);
	if (tNeedNum >= info.m_Number)
		return true;

	const GoodsFixInfo* tFix = getGoodsFixInfo(info.m_Type);
	if (nullptr == tFix)
		return false;

	uint32_t tMaxNum = 0;
	if (tFix->m_OverlapFlag)
		tMaxNum = GOODS_OVERLAP_MAX_NUM;
	else
		tMaxNum = GOODS_NOT_OVERLAP_MAX_NUM;

	tMaxNum *= emptyIndexSize();
	tMaxNum += tNeedNum;
	return tMaxNum >= info.m_Number;
}

int32_t CGLContainerBase::AutoGoods(GoodsInfo& info) {
	LOG_DEBUG("goods|type|" << info.m_Type << "|count|" << (int32_t)info.m_Number);
	MapIndexNeedGoods_t& tNeedGoods = getNeedGoods(info.m_Type);
	std::vector<MapIndexNeedGoods_t::iterator> tRm;
	for (MapIndexNeedGoods_t::iterator it = tNeedGoods.begin(); it != tNeedGoods.end(); ++it) {
		if (!isValidIndex(it->first))
			break;
		CGLGoodsBase* tGoods = it->second;
		if (tGoods->GetGoodsInfo().Equal(info)) {
			addGoods(it->first, info);
			if (tGoods->NeedNums() == 0)
				tRm.push_back(it);
			if (info.m_Number == 0)
				break;
		}
	}
	for (std::vector<MapIndexNeedGoods_t::iterator>::iterator it = tRm.begin(); it != tRm.end(); ++it) {
		MapIndexNeedGoods_t::iterator& tIt = *it;
		tNeedGoods.erase(tIt);
	}

	while (info.m_Number != 0) {
		int32_t tIndex = GetEmptyIndex();
		if (tIndex == EntityErrnoFill)
			return GLContainerErrnoNoneNotFillIndex;
		if (!isValidIndex(tIndex))
				break;
		int32_t tRet = setGoods(tIndex, info);
		if (tRet != CommonErrnoSucceed) {
			LOG_WARN("container|set|goods|errno|" << tRet);
			return tRet;
		}
	}

	return CommonErrnoSucceed;
}

int32_t CGLContainerBase::SetGoods(uint8_t box, Index_t index, GoodsInfo& info) {
	CHECK_CONTAINER_INDEX(box, index);

	return setGoods(transformIndex(box, index), info);
}

int32_t CGLContainerBase::AddGoods(uint8_t box, Index_t index, GoodsInfo& info) {
	CHECK_CONTAINER_INDEX(box, index);

	return addGoods(transformIndex(box, index), info);
}

CGLGoodsBase* CGLContainerBase::GetGoods(uint8_t box, Index_t index) const {
	CHECK_CONTAINER_INDEX_PTR(box, index);
	return GetGoods(transformIndex(box, index));
}

int32_t CGLContainerBase::RmGoods(Index_t index) {
	CGLGoodsBase* tGoods = dynamic_cast<CGLGoodsBase*>(GetChild(index));
	if (nullptr == tGoods) {
		LOG_ERROR("null|container|remove|goods");
		return GLContainerErrnoNoneGoods;
	}

	rmGoods(index, tGoods);
	rmIndexNeedGoods(tGoods->GetGoodsType(), index);
	return CommonErrnoSucceed;

}

int32_t CGLContainerBase::RmGoods(uint8_t box, Index_t index) {
	CHECK_CONTAINER_INDEX(box, index);
	return RmGoods(transformIndex(box, index));
}

int32_t CGLContainerBase::RmNumGoods(Index_t index, Number_t num) {
	CGLGoodsBase* tGoods = dynamic_cast<CGLGoodsBase*>(GetChild(index));
	if (nullptr == tGoods) {
		LOG_ERROR("null|container|remove|goods");
		return GLContainerErrnoNoneGoods;
	}

	Number_t tCurNums = tGoods->GetNums();
	if (tCurNums > num) {
		tGoods->RmNums(num);
		setIndexNeedGoods(index, tGoods);
	} else if (tCurNums == num) {
		return RmGoods(index);
	} else {
		LOG_ERROR("error|container|remove|goods|cur|" << tCurNums << "|num|" << num);
		return GLContainerErrnoGoodsNumNotEnough;
	}

	return CommonErrnoSucceed;
}

int32_t CGLContainerBase::RmNumGoods(uint8_t box, Index_t index, Number_t num) {
	CHECK_CONTAINER_INDEX(box, index);
	return RmNumGoods(transformIndex(box, index), num);
}

int32_t CGLContainerBase::UseGoods(uint8_t box, Index_t index, Value_t value) {
	CHECK_CONTAINER_INDEX(box, index);
	Index_t tBoxIndex = transformIndex(box, index);

	CGLGoodsBase* tGoods = dynamic_cast<CGLGoodsBase*>(GetChild(tBoxIndex));
	if (nullptr == tGoods) {
		LOG_ERROR("null|container|remove|goods");
		return GLContainerErrnoNoneGoods;
	}

	Value_t tCurValue = tGoods->GetValues();
	if (tCurValue < value) {
		LOG_ERROR("error|container|use|goods|cur|" << tCurValue << "|used|" << value);
		return GLContainerErrnoGoodsValueNotEnough;
	} else if (tCurValue == value) {
		return RmGoods(box, index);
	} else {
		tGoods->RmValues(value);
	}

	return CommonErrnoSucceed;
}

int32_t CGLContainerBase::SwapGoods(uint8_t firstBox, Index_t firstIndex,
		uint8_t secondBox, Index_t secondIndex) {
	CHECK_CONTAINER_INDEX(firstBox, firstIndex);
	CHECK_CONTAINER_INDEX(secondBox, secondIndex);

	Index_t tFirst = transformIndex(firstBox, firstIndex);
	Index_t tSecond = transformIndex(secondBox, secondIndex);
	CGLGoodsBase* tFirstGoods = dynamic_cast<CGLGoodsBase*>(GetChild(tFirst));
	CGLGoodsBase* tSecondGoods = dynamic_cast<CGLGoodsBase*>(GetChild(tSecond));
	swapIndexNeedGoods(tFirstGoods, tFirst, tSecondGoods, tSecond);
	SwapChild(tFirst, tSecond);
	return CommonErrnoSucceed;
}

int32_t CGLContainerBase::setGoods(Index_t boxIndex, GoodsInfo& info) {
	CGLGoodsBase* tGoods = dynamic_cast<CGLGoodsBase*>(GetChild(boxIndex));
	if (nullptr != tGoods)
		return GLContainerErrnoExistGoods;

	const GoodsFixInfo* tFix = getGoodsFixInfo(info.m_Type);
	if (nullptr == tFix)
		return CommonErrnoInter;

	tGoods = getGoods();
	tGoods->Reset();
	if (!tGoods->SetGoodsInfo(info, tFix)) {
		setGoods(tGoods);
		return GLContainerErrnoSetNumTooMany;
	}
	SetChild(boxIndex, tGoods);
	setIndexNeedGoods(boxIndex, tGoods);

	return CommonErrnoSucceed;
}

int32_t CGLContainerBase::addGoods(Index_t boxIndex, GoodsInfo& info) {
	CGLGoodsBase* tGoods = dynamic_cast<CGLGoodsBase*>(GetChild(boxIndex));
	if (nullptr == tGoods) {
		return setGoods(boxIndex, info);
	} else {
		if (!tGoods->GetGoodsInfo().Equal(info)) {
			LOG_ERROR("error|container|set|not|equal");
			return GLContainerErrnoHasDiffGoods;
		}
		if (!tGoods->IsOverlap()) {
			LOG_ERROR("error|container|set|not|equal");
			return GLContainerErrnoNotOverlap;
		}

		Number_t tNeedNum = tGoods->NeedNums();
		if (tNeedNum >= info.m_Number) {
			tGoods->AddNums(info.m_Number);
			info.m_Number = 0;
		} else {
			tGoods->AddNums(tNeedNum);
			info.m_Number -= tNeedNum;
		}
		setIndexNeedGoods(boxIndex, tGoods);
	}

	return CommonErrnoSucceed;
}

